package com.leigq.basaltools.util;

import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件工具
 * <br/>
 * 参考：<a href="https://www.zhangshengrong.com/p/9MNloWd7aJ/">java实现文件切片和合并的代码示例</a>
 *
 * @author leiguoqing
 * @date 2021/09/02
 */
public class FileTools {

    /**
     * 使用默认 ".tmp" 后缀分割文件，分割后的文件最终输出到传入文件的同级目录下
     * <br/>
     * 最终拆分后的名称格式为: {原文件名称}_{文件索引，从0开始}.tmp
     * 例如：把文件名为：“ 1.txt ” 拆分为2个文件，则得到 1.txt_0.tmp 和 1.txt_1.tmp　这两个文件
     *
     * @param pathname 文件路径
     * @param count    最终拆分个数
     * @return {@link List} 拆分的文件路径集合
     */
    public static List<String> split(String pathname, int count) throws IOException {
        return split(pathname, count, null);
    }


    /**
     * 使用指定后缀分割文件，分割后的文件最终输出到传入文件的同级目录下
     * <br/>
     * 最终拆分后的名称格式为: {原文件名称}_{文件索引，从0开始}.{suffix}
     * 例如：把文件名为：“ 1.txt ” 拆分为2个文件，则得到 1.txt_0.{suffix} 和 1.txt_1.{suffix}　这两个文件
     *
     * @param pathname 文件路径
     * @param count    最终拆分个数
     * @param suffix   拆分的文件后缀，默认为 ".tmp"
     * @return {@link List} 拆分的文件路径集合
     */
    public static List<String> split(String pathname, int count, String suffix) throws IOException {
        if (!StringUtils.hasText(pathname)) {
            throw new IllegalArgumentException("pathname can not be null");
        }

        if (count < 1) {
            throw new IllegalArgumentException("count must greater than 1");
        }

        if (!StringUtils.hasText(suffix)) {
            suffix = "tmp";
        }

        final File file = new File(pathname);

        // 预分配文件所占用的磁盘空间，在磁盘创建一个指定大小的文件，“r”表示只读，“rw”支持随机读写
        RandomAccessFile raf = new RandomAccessFile(file, "r");
        // 计算文件大小，单位：Byte
        long length = raf.length();

        // 计算文件切片后每一份文件的大小，单位：Byte
        long maxSize = length / count;

        // 定义初始文件的偏移量(读取进度)
        long offset = 0L;

        // 保存分割后的文件名称集合
        List<String> splitFileNames = new ArrayList<>(count);

        // 开始切割文件，count-1 最后一份文件不处理
        for (int i = 0; i < count - 1; i++) {
            // 标记初始化
            long fileBegin = offset;
            // 分割第几份文件
            long fileEnd = (i + 1) * maxSize;

            // 分割后的文件名称: {原文件名称}_{文件索引，从0开始}.{suffix}
            String splitFilePathname = pathname + "_" + i + "." + suffix;

            splitFileNames.add(splitFilePathname);

            // 写入文件
            offset = write(pathname, splitFilePathname, fileBegin, fileEnd);
        }

        // 剩余部分文件写入到最后一份(如果不能平平均分配的时候)
        if (length - offset > 0) {
            String splitFilePathname = pathname + "_" + (count - 1) + "." + suffix;
            splitFileNames.add(splitFilePathname);
            // 写入文件
            write(pathname, splitFilePathname, offset, length);
        }
        return splitFileNames;
    }


    /**
     * 指定文件每一份的边界，写入不同文件中
     *
     * @param pathname          源文件路径
     * @param splitFilePathname 拆分的文件路径
     * @param begin             开始指针的位置
     * @param end               结束指针的位置
     * @return long 当前读取文件的指针
     */
    private static long write(String pathname, String splitFilePathname, long begin, long end) throws IOException {
        // 申明文件切割后的文件磁盘
        RandomAccessFile in = new RandomAccessFile(new File(pathname), "r");

        // 定义一个可读，可写的文件并且后缀名为.tmp的二进制文件
        RandomAccessFile out = new RandomAccessFile(new File(splitFilePathname), "rw");

        // 申明具体每一文件的字节数组
        byte[] b = new byte[1024];

        int n;

        // 从指定位置读取文件字节流
        in.seek(begin);

        // 判断文件流读取的边界
        while (in.getFilePointer() <= end && (n = in.read(b)) != -1) {
            // 从指定每一份文件的范围，写入不同的文件
            out.write(b, 0, n);
        }

        //定义当前读取文件的指针
        long endPointer = in.getFilePointer();

        // 关闭输入流
        in.close();

        // 关闭输出流
        out.close();

        return endPointer;
    }
}
